use crate::hoops::jwt;
use crate::AppError;
use rinja::Template;
use salvo::oapi::extract::*;
use salvo::prelude::*;
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::models::user::SafeUser;
use crate::{service, empty_ok, json_ok, AppResult, EmptyResult, JsonResult};

#[derive(Template)]
#[template(path = "user_list_page.html")]
pub struct UserListPageTemplate {}

#[derive(Template)]
#[template(path = "user_list_frag.html")]
pub struct UserListFragTemplate {}

#[handler]
pub async fn list_page(req: &mut Request, res: &mut Response) -> AppResult<()> {
    let is_fragment = req.headers().get("X-Fragment-Header");
    if let Some(cookie) = res.cookies().get("jwt_token") {
        let token = cookie.value().to_string();
        if !jwt::decode_token(&token) {
            res.render(Text::Plain("Invalid token"));
            return Ok(());
        }
    }

    Ok(())
}

#[derive(Deserialize, Debug, Validate, Default, ToSchema)]
pub struct CreateInData {
    #[validate(length(min = 5, message = "username length must be greater than 5"))]
    pub username: String,
    #[validate(length(min = 6, message = "password length must be greater than 5"))]
    pub password: String,
    pub avatar: String,
    pub introduction: String,
}
#[endpoint(tags("users"))]
pub async fn create_user(req: &mut Request, idata: JsonBody<CreateInData>) -> JsonResult<SafeUser> {
    let CreateInData {
        username,
        password,
        avatar,
        introduction,
    } = idata.into_inner();
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req)?;
    let db = service::pool();
    let user =
        service::rbac::user::UserService::create(db, &username, &password, &avatar, &introduction, tenant_id)
            .await?;

    json_ok(SafeUser {
        tenant_id: user.tenant_id,
        id: Some(user.id),
        username: Some(user.username),
        create_by: Some(user.create_by),
        create_time: Some(user.create_time),
        update_by: Some(user.update_by),
        update_time: Some(user.update_time),
        avatar: Some(user.avatar),
        introduction: Some(user.introduction),
    })
}

#[derive(Deserialize, Debug, Validate, ToSchema)]
struct UpdateInData {
    #[validate(length(min = 5, message = "username length must be greater than 5"))]
    username: String,
    #[validate(length(min = 6, message = "password length must be greater than 5"))]
    password: String,
    avatar: String,
    introduction: String,
}
#[endpoint(tags("users"), parameters(("user_id", description = "user id")))]
pub async fn update_user(
    req: &mut Request,
    id: PathParam<i64>,
    idata: JsonBody<UpdateInData>,
) -> JsonResult<SafeUser> {
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req)?;
    let id = id.into_inner();
    let UpdateInData {
        username,
        password,
        avatar,
        introduction,
    } = idata.into_inner();
    let db = service::pool();
    let user = service::rbac::user::UserService::update(
        db,
        &id,
        Some(&username),
        Some(&password),
        Some(&avatar),
        Some(&introduction),
        tenant_id,
    )
    .await?;
    json_ok(SafeUser {
        tenant_id: user.tenant_id,
        id: Some(user.id),
        username: Some(user.username),
        create_by: Some(user.create_by),
        create_time: Some(user.create_time),
        update_by: Some(user.update_by),
        update_time: Some(user.update_time),
        avatar: Some(user.avatar),
        introduction: Some(user.introduction),
    })
}

#[endpoint(tags("users"))]
pub async fn delete_user(req: &mut Request, id: PathParam<i64>) -> EmptyResult {
    let id = id.into_inner();
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req)?;
    let db = service::pool();
    service::rbac::user::UserService::delete(db, &id, tenant_id).await?;
    empty_ok()
}

#[derive(Debug, Deserialize, Validate, Extractible)]
#[salvo(extract(default_source(from = "query")))]
pub struct UserListQuery {
    pub username: Option<String>,
    #[serde(default = "default_page")]
    pub current_page: i64,
    #[serde(default = "default_page_size")]
    pub page_size: i64,
}

fn default_page() -> i64 {
    1
}
fn default_page_size() -> i64 {
    10
}

#[derive(Debug, Serialize, ToSchema)]
pub struct UserListResponse {
    pub data: Vec<SafeUser>,
    pub total: i64,
    pub current_page: i64,
    pub page_size: i64,
}

#[endpoint(tags("users"))]
pub async fn list_users(req: &mut Request) -> JsonResult<UserListResponse> {
    let db = service::pool();
    let query: UserListQuery = req.extract().await?;
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req)?;
    let username_filter = query.username.as_deref();

    let (users, total) = service::rbac::user::UserService::find_all(
        db,
        username_filter,
        tenant_id,
        query.current_page as u64,
        query.page_size as u64,
    )
    .await?;

    json_ok(UserListResponse {
        data: users,
        total: total as i64,
        current_page: query.current_page,
        page_size: query.page_size,
    })
}

#[derive(Serialize, ToSchema)]
pub struct UserWithRoles {
    id: Option<i64>,
    username: Option<String>,
    create_by: Option<String>,
    create_time: Option<chrono::NaiveDateTime>,
    update_by: Option<String>,
    update_time: Option<chrono::NaiveDateTime>,
    avatar: Option<String>,
    introduction: Option<String>,
    roles: Vec<String>,
}

#[endpoint(tags("users"), parameters(("id", description = "user id")))]
pub async fn detail_user(req: &mut Request, id: PathParam<i64>) -> JsonResult<UserWithRoles> {
    let db = service::pool();
    let user_id = id.into_inner();
    use crate::utils::tenant::get_tenant_id;
    let tenant_id = get_tenant_id(req)?;

    let user = service::rbac::user::UserService::find_by_id(db, user_id, tenant_id)
        .await?
        .ok_or_else(|| AppError::Public("User not found".to_string()))?;

    let tenant_id = tenant_id.ok_or_else(|| {
        tracing::error!("Tenant ID is missing");
        StatusError::bad_request().brief("Tenant ID is required")
    })?;

    let roles = crate::service::rbac::user_role::UserRoleService::find_roles_by_user(
        db,
        &user_id.to_string(),
        tenant_id,
    )
    .await?;

    json_ok(UserWithRoles {
        id: Some(user.id),
        username: Some(user.username),
        create_by: Some(user.create_by),
        create_time: Some(user.create_time),
        update_by: Some(user.update_by),
        update_time: Some(user.update_time),
        avatar: Some(user.avatar),
        introduction: Some(user.introduction),
        roles,
    })
}
